/**
 * 享元模式：运用共享技术有效地支持大量细粒度的对象。在有大量对象时，把其中共同的部分抽象出来，如果有相同的业务请求，直接返回内存中已有的对象，避免重新创建。
 * 程序必须支持大量对象且没有足够的内存容量时使用享元模式。
 * 应用该模式所获的收益大小取决于使用它的方式和情景。 它在下列情况中最有效：
 * 1. 程序需要生成数量巨大的相似对象
 * 2. 这将耗尽目标设备的所有内存
 * 3. 对象中包含可抽取且能在多个对象间共享的重复状态。
 * 对于享元模式，需要将对象的信息分为两个部分：内部状态和外部状态。内部状态是指被共享出来的信息，储存在享元对象内部且不随环境变化而改变；外部状态是不可以共享的，它随环境改变而改变，是由客户端控制的。
 * 可以有选择地创建工厂类来管理享元缓存池，它负责在新建享元时检查已有的享元。如果选择使用工厂，客户端就只能通过工厂来请求享元，它们需要将享元的内在状态作为参数传递给工厂。
 */
#pragma once

#include <iostream>
#include <memory>
#include <map>

using namespace std;

namespace flyweight
{
//抽象享元类，提供享元类外部接口。
class Flyweight
{
public:
    virtual ~Flyweight(){}
    virtual void setArticle(const string&) = 0;
    virtual const string& article() = 0;
};

//具体的享元类
class ConcreteFlyweight : public Flyweight
{
public:
    ConcreteFlyweight(const string& name) : m_user(name) {}
    void setArticle(const string& info) override {
        m_article = info;
    }
    const string& article() override {
        return m_article;
    }

private:
    string m_user;
    string m_article;
};

//享元工厂类,使用map管理享元对象
class FlyweightFactory
{
public:
    ~FlyweightFactory() {
         m_consumerMap.clear();
    }

    void hosting(const string& user, const string& article) {
        if(m_consumerMap.count(user)) {
            cout << "A customer named " << user.data() << " already exists" << endl;
            m_consumerMap.at(user)->setArticle(article);
        } else {
            shared_ptr<ConcreteFlyweight> consumer(new ConcreteFlyweight(user));
            consumer.get()->setArticle(article);
            m_consumerMap.insert(pair<string, shared_ptr<ConcreteFlyweight>>(user, consumer));
        }
    }

    void display() {
        map<string, shared_ptr<ConcreteFlyweight>>::iterator iter = m_consumerMap.begin();
        for(; iter != m_consumerMap.end(); iter++) {
            cout << iter->first.data() << ": "<< iter->second.get()->article().data() << endl;
        }
    }

private:
    map<string, shared_ptr<ConcreteFlyweight>> m_consumerMap;
};

void ClientCode()
{
    unique_ptr<FlyweightFactory> ts(new FlyweightFactory);
    ts->hosting("zhangsan", "computer");
    ts->hosting("lisi", "phone");
    ts->hosting("wangwu", "watch");

    ts->display();

    ts->hosting("zhangsan", "TT");
    ts->hosting("lisi", "TT");
    ts->hosting("wangwu", "TT");

    ts->display();
}
}   // namespace flyweight